import unittest
import operator, math
from functools import reduce 
from itertools import combinations, starmap, product, permutations


class number_gen():
	i = 0
	val = 0

	def __init__(self): pass

	def __iter__(self): 
		self.i = 0
		self.val = 0
		return self

	def __next__(self):
		self.i += 1
		self.val += self.i
		return self.val

def prime_factors(n):
	factors = []
	factor = 2 

	while factor<=n:

		if n%factor == 0:
			factors.append(factor)
			n /= factor
		else: 
			factor += 1 

	return factors

def divisor(n):
	if n<=0: raise ValueError
	if n==1: return [1]

	factors = []

	for i in range(1,n+1):
		if n%i==0: factors.append(i)

	#print(factors)
	return list(set(factors))

def num_of_divisor(n):
	if n<=0: raise ValueError
	if n==1: return 1 

	factors = prime_factors(n)
	prime_factors_exp = [factors.count(i) for i in set(factors)]

	prime_factors_exp = list( map(lambda x: x+1, prime_factors_exp) )

	n = 1
	for i in prime_factors_exp: n *= i
	return n


class test_generator(unittest.TestCase):

	right_result = {1:1, 2:3, 3:6, 4:10, 5:15, 6:21, 7:28}
	n = number_gen()

	def test_iterable(self):
		self.assertTrue(iter(self.n) != None)
		self.assertTrue(next(self.n) != None)

	def test_know_value(self):
		itr = iter(self.n)
		i = 0
		for v,k in self.right_result.items():
			i = next(itr)
			self.assertEqual(i, k)
	
	def test_generated_value(self):
		itr = iter(self.n)
		for i in range(1,1000):
			r = 0 
			for j in range(i+1):
				r += j
			self.assertEqual(r, next(itr))	

class test_divisor(unittest.TestCase):
	
	def test_known_prime_factors(self):
		known_prime_factor = {6:[2,3], 15:[3,5], 30:[2,3,5], 10:[2,5]}
		for k,v in known_prime_factor.items():
			self.assertEqual(prime_factors(k), v) 

	def test_good_result_prime_factors(self):
		for i in range(2,1000):
			factors = prime_factors(i)
			isi = reduce(lambda x,y: x*y, factors)
			self.assertEqual(i, isi)

	def test_primes_number(self):
		def is_prime(n):
			return not [x for x in range(2,int(math.sqrt(n)+1)) if n%x==0]

		for i in range(2,1000):
			if is_prime(i):
				self.assertEqual(prime_factors(i),[i])

	def test_good_result_divisor(self):
		for i in range(2,1000):
			divisors = divisor(i)
			for j in divisors:
				self.assertTrue(i%j==0)

	def test_zeros_divisors(self): 
		self.assertRaises(ValueError, divisor, 0)
		self.assertEqual(divisor(1), [1])
		for i in range(-1000,0,-1):
			self.assertRaises(ValueError, divisor, i)
		
	def test_complete_result_divisor(self):
		for i in range(2,2000): #930930):
			divisors = divisor(i)
			manual_divisors = []
			j = 1
			for j in range(1,i+1):
				if i%j==0: manual_divisors.append(j)
			self.assertEqual(sorted(divisors), sorted(manual_divisors),\
					 "test_complete_result_divisor {0}: manual {1} - {2}".\
					  format(i, manual_divisors, divisors))
	def test_known_divisor(self):
		known_vals = {1:[1], 3:[1,3], 6:[1,2,3,6], 10:[1,2,5,10], 15:[1,3,5,15], 21:[1,3,7,21], 28:[1,2,4,7,14,28]}
		for k,v in known_vals.items():
			
			divisors = divisor(k)
			self.assertTrue(sorted(divisors) == sorted(v))
			self.assertTrue(num_of_divisor(k) == len(v), "{0} {1} - {2}".format(num_of_divisor(k), len(v), k))

	def test_big_number(self):
		k = 930930
		manual_divisors = []
		for i in range(1,k+1):
			if k%i==0: manual_divisors.append(i)
		divs = divisor(k)
		self.assertTrue(sorted(divs) == sorted(manual_divisors),\
				"test_big_number:\n divisors: {0} \n manuals:  {1}".format(sorted(divs), sorted(manual_divisors)))
		self.assertTrue(len(manual_divisors), num_of_divisor(k))
		

if __name__ == "__main__":
	#unittest.main()

	n = number_gen()
	for i in n:
		#l = divisor(i)
		m = num_of_divisor(i)
		print("{0} has {1} divisor.".format(i,m))
		if m>=500: 
			break
